/*
Miranda IM History Sweeper plugin
Copyright (C) 2002-2007  Sergey V. Gershovich
Copyright (C) 2006-2007  borkra

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "historysweeper.h"
#include <time.h>

// build criteria
unsigned BuildCriteria(int dwti)
{
	time_t tim =  time(NULL);
	struct tm gtim = *gmtime(&tim);

	switch (dwti)
	{
		case 1:
			gtim.tm_mday -= 1;
			break;
		case 2:
			gtim.tm_mday -= 3;
			break;
		case 3:
			gtim.tm_mday -= 7;
			break;
		case 4:
			gtim.tm_mday -= 14;
			break;
		case 5:
			gtim.tm_mon -= 1;
			break;
		case 6:
			gtim.tm_mon -= 3;
			break;
		case 7:
			gtim.tm_mon -= 6;
			break;
		case 8:
			gtim.tm_year -= 1;
			break;
	}
	return mktime(&gtim);
}

typedef struct
{
    HANDLE hDBEvent;
    DWORD CRC32;
    DWORD Timestamp;
} BEventData;

static int CompareBookmarks( const void* p1, const void* p2 )
{
	return (BYTE*)((BEventData*)p2)->hDBEvent - (BYTE*)((BEventData*)p1)->hDBEvent;
}

static void GetBookmarks(HANDLE hContact, DWORD tm, BEventData** books, size_t* bookcnt )
{
	DBVARIANT dbv;
	DBCONTACTGETSETTING cgs;

	*books = NULL;
	*bookcnt = 0;

	cgs.szModule = "HistoryPlusPlus";
	cgs.szSetting = "Bookmarks";
	cgs.pValue = &dbv;
	if (CallService(MS_DB_CONTACT_GETSETTING,(WPARAM)hContact,(LPARAM)&cgs) == 0)
	{
		if (dbv.cpbVal > 2 && *(WORD*)dbv.pbVal >= sizeof(BEventData))
		{
			size_t recSize = *(WORD*)dbv.pbVal;
			size_t numb = (dbv.cpbVal - sizeof(WORD)) / recSize;

			BEventData* tbooks = mir_alloc(sizeof(BEventData)*numb);
			size_t tbookcnt = 0;

			size_t i;
			BYTE* evs = dbv.pbVal + sizeof(WORD);

			for (i=0; i<numb; ++i)
			{
				// we want to keep all bookmarks
				//if (((BEventData*)evs)->Timestamp <= tm)
					tbooks[tbookcnt++] = *(BEventData*)evs;
				evs += recSize;
			}
			if (tbookcnt != 0)
			{
				qsort(tbooks, tbookcnt, sizeof(BEventData), CompareBookmarks);
				*bookcnt = tbookcnt;
				*books = tbooks;
			}
			else
				mir_free(tbooks);
		}
		DBFreeVariant(&dbv);
	}
}

BOOL DeleteThisEventType(DBEVENTINFO* dbei)
{
	BOOL doDelete = FALSE;
	
	switch (dbei->eventType)
	{
	case EVENTTYPE_MESSAGE:
		doDelete = (dbei->flags & DBEF_SENT) ? 
			hs_events.EventsOutgouingMessages : 
			hs_events.EventsIncomingMessages;
		break;
	case EVENTTYPE_URL:
		doDelete = hs_events.EventsURL;
		break;
	case EVENTTYPE_FILE:
		doDelete = hs_events.EventsFiles;
		break;
	case EVENTTYPE_CONTACTS:
		doDelete = hs_events.EventsContacts;
		break;
	case EVENTTYPE_STATUSCHANGE:
		doDelete = hs_events.EventsStatus;
		break;
	case EVENTTYPE_NICKNAMECHANGE:
		doDelete = hs_events.EventsNick;
		break;
	case EVENTTYPE_STATUSMESSAGECHANGE:
		doDelete = hs_events.EventsStatusMessage;
		break;
	case EVENTTYPE_AVATARCHANGE:
		doDelete = hs_events.EventsAvatar;
		break;
	case EVENTTYPE_VOICE_CALL:
		doDelete = hs_events.EventsVoiceCall;
		break;
	case ICQEVENTTYPE_SELF_REMOVE:
		doDelete = hs_events.EventsICQRemove;
		break;
	case ICQEVENTTYPE_CLIENT_CHANGE:
		doDelete = hs_events.EventsICQClientChange;
		break;
	case ICQEVENTTYPE_IGNORECHECK_STATUS:
		doDelete = hs_events.EventsICQIgnoreCheck;
		break;
	case ICQEVENTTYPE_FUTURE_AUTH:
		doDelete = hs_events.EventsICQAuth;
		break;
	case ICQEVENTTYPE_CHECK_STATUS:
		doDelete = hs_events.EventsICQCheckStatus;
		break;
	default:
		doDelete = hs_events.EventUnknown;
		break;
/*
	case EVENTTYPE_CONTACTLEFTCHANNEL
	case EVENTTYPE_SMTPSIMPLE:
	doDelete = hs_events.EventsWATrack
*/
	}
	
	return doDelete;
}

//Sweep history from specified contact
void SweepHistoryFromContact(HANDLE hContact, DWORD Criteria, BOOL BatchJob)
{
	BOOL unsafe = DBGetContactSettingByte(NULL, ModuleName, "UnsafeMode",0);

	//Get first event in chain
	HANDLE hDBEvent = (HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0);
	if (hDBEvent != NULL) 
	{ 
		BEventData* books; 
		size_t bookcnt;

		// ask user (if not batchjob)
		if (!BatchJob)
		{	// it's bad idea to use modal message boxes ?
			HWND hwndCLC = (HWND)CallService(MS_CLUI_GETHWND, 0, 0);
			if (MessageBox(hwndCLC, Translate("Sweep history messages?"), Translate(ModuleName),
				MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2) != IDYES) return;
		}

		SetCursor(LoadCursor(0,IDC_WAIT));
	
		// Get Bookmarks from H++ 
		GetBookmarks(hContact, Criteria, &books, &bookcnt);

		// switch off SAFETYMODE if necessary
		if (unsafe)	CallService(MS_DB_SETSAFETYMODE,0,0); 

		while (hDBEvent != NULL) 
		{
			DBEVENTINFO dbei = {0}; 
			BEventData* item = NULL;
			BOOL doDelete = TRUE;
			HANDLE hDBEventNext;

			if (bookcnt != 0)
			{
				BEventData ev = { hDBEvent, 0, 0 };
				item = (BEventData*)bsearch( &ev, books, bookcnt, sizeof(BEventData), CompareBookmarks);
			}
			

			dbei.cbSize = sizeof(dbei);
			CallService(MS_DB_EVENT_GET, (WPARAM)hDBEvent, (LPARAM)&dbei);

			// check event type
			doDelete = DeleteThisEventType(&dbei);
			
			// if it approach to criteria - delete it
			if (Criteria != 0 || item != NULL)
			{

				if (Criteria != 0 && Criteria < dbei.timestamp) break;
				
				if (item != NULL && !hs_events.KeepHppBookMarks)
				{
					size_t btshift = (--bookcnt - (item - books))*sizeof(BEventData);
					doDelete = item->Timestamp != dbei.timestamp;
					if (btshift) memmove(item, item+1, btshift);
				}
			}
		
			// find next event handle before delete 
			hDBEventNext = (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hDBEvent, 0);

			// Delete event if it pass all checks
			if (doDelete)
				CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDBEvent);
			
			hDBEvent = hDBEventNext;
		} 

		mir_free(books);

		// switch ON safety mode as fast as we can
		// to avoid  DB corruption
		if (unsafe)	CallService(MS_DB_SETSAFETYMODE, 1, 0);

		SetCursor(LoadCursor(0,IDC_ARROW));
	}
}

// sweep system history
static void SweepSystemHistory(DWORD dwCriteria, BOOL bBatchJob)
{
	SweepHistoryFromContact(NULL, dwCriteria, bBatchJob);
}

// sweep histroy from selected contacts
static void SweepHistoryFromSelectedContacts(DWORD dwCriteria, BOOL bBatchJob)
{
	//find first contact
	HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
	while ( hContact != NULL )
	{
		// if it ticked in options page
		if (DBGetContactSettingByte(hContact, "CList", "SweepHistory", 0))
			// sweep history from it
			SweepHistoryFromContact(hContact, dwCriteria, bBatchJob);

		hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
	}
}

static void SweepWholeHistory(DWORD dwCriteria, BOOL bBatchJob)
{
	// find first contact
	HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
	while ( hContact != NULL )
	{
		//sweep history from it
		SweepHistoryFromContact (hContact, dwCriteria, bBatchJob);
		hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
	}

	// sweep system history
	SweepSystemHistory(dwCriteria, bBatchJob);
}

// stratup/shutdown action
void SSAction(void)
{ 
	DWORD dwCriteria = BuildCriteria(DBGetContactSettingByte(NULL,ModuleName,"StartupShutdownOlder",0));
	switch (DBGetContactSettingByte(NULL,ModuleName,"StartupShutdownAction",0))
	{
		case 0: 
			SweepSystemHistory(dwCriteria, TRUE);
			break;

		case 1: 
			SweepWholeHistory(dwCriteria, TRUE);
			break;

		case 2: 
			SweepHistoryFromSelectedContacts(dwCriteria, TRUE);
			break;
	}
}

// Main menu action
int MainMenuAction(WPARAM wParam,LPARAM lParam)
{
	DWORD dwCriteria = BuildCriteria(DBGetContactSettingByte(NULL,ModuleName,"MainMenuOlder",0));
	switch (DBGetContactSettingByte(NULL,ModuleName,"MainMenuAction",0))
	{
		case 0:
			if (MessageBox ((HWND)CallService(MS_CLUI_GETHWND,0,0),
				Translate("Sweep system history?"),Translate(ModuleName),
				MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDYES)
				SweepSystemHistory(dwCriteria,TRUE);
			break;

		case 1:
			if (MessageBox ((HWND)CallService(MS_CLUI_GETHWND,0,0),
				Translate("Sweep whole history?"),Translate(ModuleName),
				MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDYES)
				SweepWholeHistory(dwCriteria,TRUE);
			break;

		case 2:
			if (MessageBox ((HWND)CallService(MS_CLUI_GETHWND,0,0),
				Translate("Sweep history from selected contacts?"),Translate(ModuleName),
				MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2) == IDYES)
				SweepHistoryFromSelectedContacts(dwCriteria,TRUE);
			break;
	}
	return 0;
}

// Contact menu action
int ContactMenuAction(WPARAM wParam,LPARAM lParam)
{
	SweepHistoryFromContact ((HANDLE)wParam, 0, FALSE);
	return 0;
}
